/*
 * =====================================================================================
 *
 *       Filename:  gdc.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  12/12/2011 10:16:38 AM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *
 * =====================================================================================
 */

#include "gdc.h"

/* 一级cache和二级cache空闲块节点 */
GDCFreeNode_ST g_stGDCFreeNode[NGE_MAX_THREAD + 1];


static GDCNode_ST *GDC_NodeCreate(void)
{
    GDCNode_ST *pstNode = (GDCNode_ST *)GDC_MALLOC(sizeof(GDCNode_ST));

    if (NULL == pstNode)
    {
        return NULL;
    }

    memset(pstNode, 0, sizeof(GDCNode_ST));
    INIT_LIST_HEAD(&pstNode->list);

    return pstNode;
}

static INT32 GDC_NodeFree(GDCNode_ST **pstNode)
{
    if (*pstNode)
    {
        GDC_FREE(*pstNode);
        *pstNode = NULL;
    }

    return GDC_OK;
}

INT32 GDC_Init(UINT32 uiThreadNum)
{
    INT32 i = 0;
    INT32 j = 0;
    INT32 iRet = GDC_OK;
    GDCNode_ST *pstNode = NULL;
    INT32 iPos = GDC_NODE_8_SIZE * GDC_NODE_8_NUM;
    INT32 iCacheLen = GDC_NODE_8_SIZE * GDC_NODE_8_NUM + GDC_NODE_32_SIZE * GDC_NODE_32_NUM;

    /** 
     * assign level1 cache node 
     **/
    for (i = 0; i < NGE_MAX_THREAD; ++i)
    {
        INIT_LIST_HEAD(&g_stGDCFreeNode[i].list_node8);
        INIT_LIST_HEAD(&g_stGDCFreeNode[i].list_node32);
        g_stGDCFreeNode[i].pcCache = (CHAR *)GDC_MALLOC(iCacheLen);
        if (NULL == g_stGDCFreeNode[i].pcCache)
        {
            GDC_PRINTF("malloc error !\n");
            return GDC_ERR;
        }

        /* assign 8bit node pointer */
        for (j = 0; j < GDC_NODE_8_NUM; ++j)
        {
            pstNode = GDC_NodeCreate();
            if (NULL == pstNode)
            {
                GDC_PRINTF("GDC node create error !\n");
                return GDC_ERR;
            }

            pstNode->enType = GDC_NODE_8;
            pstNode->enLevel = GDC_NODE_Level1;
            pstNode->pcMem = g_stGDCFreeNode[i].pcCache + j * GDC_NODE_8_SIZE;
            GDC_LIST_ADD_TAIL(&pstNode->list, &g_stGDCFreeNode[i].list_node8);
        }

        /* assign 32bit node pointer */
        for (j = 0; j < GDC_NODE_32_NUM; ++j)
        {
            pstNode = GDC_NodeCreate();
            if (NULL == pstNode)
            {
                GDC_PRINTF("GDC node create error !\n");
                return GDC_ERR;
            }

            pstNode->enType = GDC_NODE_32;
            pstNode->enLevel = GDC_NODE_Level1;
            pstNode->pcMem = g_stGDCFreeNode[i].pcCache + iPos + j * GDC_NODE_32_SIZE;
            GDC_LIST_ADD_TAIL(&pstNode->list, &g_stGDCFreeNode[i].list_node32);
        }
    }
        
    /**
     * assign level2 cache node 
     **/
    iPos = GDC_NODE_8_SIZE * GDC_L2_NODE_8_NUM;
    iCacheLen = GDC_NODE_8_SIZE * GDC_L2_NODE_8_NUM + GDC_NODE_32_SIZE * GDC_L2_NODE_32_NUM;

    i = NGE_MAX_THREAD;
    INIT_LIST_HEAD(&g_stGDCFreeNode[i].list_node8);
    INIT_LIST_HEAD(&g_stGDCFreeNode[i].list_node32);
    g_stGDCFreeNode[i].pcCache = (CHAR *)GDC_MALLOC(iCacheLen);
    if (NULL == g_stGDCFreeNode[i].pcCache)
    {
        GDC_PRINTF("malloc error !\n");
        return GDC_ERR;
    }

    /* assign 8bit node pointer */
    for (j = 0; j < GDC_L2_NODE_8_NUM; ++j)
    {
        pstNode = GDC_NodeCreate();
        if (NULL == pstNode)
        {
            GDC_PRINTF("GDC node create error !\n");
            return GDC_ERR;
        }

        pstNode->enType = GDC_NODE_8;
        pstNode->enLevel = GDC_NODE_Level2;
        pstNode->pcMem = g_stGDCFreeNode[i].pcCache + j * GDC_NODE_8_SIZE;
        GDC_LIST_ADD_TAIL(&pstNode->list, &g_stGDCFreeNode[i].list_node8);
    }

    /* assign 32bit node pointer */
    for (j = 0; j < GDC_L2_NODE_32_NUM; ++j)
    {
        pstNode = GDC_NodeCreate();
        if (NULL == pstNode)
        {
            GDC_PRINTF("GDC node create error !\n");
            return GDC_ERR;
        }

        pstNode->enType = GDC_NODE_32;
        pstNode->enLevel = GDC_NODE_Level2;
        pstNode->pcMem = g_stGDCFreeNode[i].pcCache + iPos + j * GDC_NODE_32_SIZE;
        GDC_LIST_ADD_TAIL(&pstNode->list, &g_stGDCFreeNode[i].list_node32);
    }

    return iRet; 
}

INT32 GDC_Cleanup(void)
{
    INT32 i = 0;
    GDCNode_ST *pstNode = NULL;
    GDCNode_ST *pstNextNode = NULL;

    /* free level 1+2 cache */
    for (i = 0; i < NGE_MAX_THREAD + 1; ++i)
    {
        GDC_LIST_EACH_ENTRY_SAFE(pstNode, pstNextNode, &g_stGDCFreeNode[i].list_node8, list)
        {
            GDC_LIST_DEL(&pstNode->list);
            GDC_NodeFree(&pstNode);
        }
        
        GDC_LIST_EACH_ENTRY_SAFE(pstNode, pstNextNode, &g_stGDCFreeNode[i].list_node32, list)
        {
            GDC_LIST_DEL(&pstNode->list);
            GDC_NodeFree(&pstNode);
        }

        if (g_stGDCFreeNode[i].pcCache)
        {
            GDC_FREE(g_stGDCFreeNode[i].pcCache);
            g_stGDCFreeNode[i].pcCache = NULL;
        }
    }

    return GDC_OK;
}

GDC_ST *GDC_Create(UINT32 uiMaxLen, UINT32 uiModule)
{
    GDC_ST *pstGDC = (GDC_ST *)GDC_MALLOC(sizeof(GDC_ST));

    if (NULL == pstGDC)
    {
        return NULL;
    }

    memset(pstGDC, 0, sizeof(GDC_ST));
    INIT_LIST_HEAD(&pstGDC->list);
    pstGDC->uiMaxLen = uiMaxLen;
    pstGDC->uiModule = uiModule;

    return pstGDC;
}

INT32 GDC_Free(GDC_ST *pstGDC)
{
    GDCNode_ST *pstNode = NULL;
    GDCNode_ST *pstNextNode = NULL;

    /* free each cache node */
    GDC_LIST_EACH_ENTRY_SAFE(pstNode, pstNextNode, &pstGDC->list, list)
    {
        GDC_LIST_DEL(&pstNode->list);
        GDC_NodeFree(&pstNode);
    }

    /* free cache buffer */
    if (pstGDC->pcCacheBuf)
    {
        GDC_FREE(pstGDC->pcCacheBuf);
        pstGDC->pcCacheBuf = NULL;
    }

    /* free gdc self */
    GDC_FREE(pstGDC);

    return GDC_OK;
}

INT32 GDC_FreeCache(GDC_ST *pstGDC)
{
    GDCNode_ST *pstNode = NULL;
    GDCNode_ST *pstNextNode = NULL;

    /* release cache list to free list */
    GDC_LIST_EACH_ENTRY_SAFE(pstNode, pstNextNode, &pstGDC->list, list)
    {
        GDC_LIST_DEL(&pstNode->list);

        /* TODO: back to free list */
    }
    
    return GDC_OK;
}

UINT32 GDC_GetCacheLen(GDC_ST *pstGDC)
{
    return pstGDC->uiCacheUse;
}

UINT32 GDC_GetCacheMaxLen(GDC_ST *pstGDC)
{
    return pstGDC->uiMaxLen;
}   

static inline GDCNode_ST *GDC_GetLevel1Cache(INT32 iThreadID, INT32 iBlockSize)
{
    GDCNode_ST *pstNode = NULL;
    GDCNode_ST *pstNextNode = NULL;

    /* level 1 cache no need lock */
    if (GDC_NODE_32_SIZE == iBlockSize)
    {
        GDC_LIST_EACH_ENTRY_SAFE(pstNode, pstNextNode, &g_stGDCFreeNode[iThreadID].list_node32, list)
        {
            GDC_LIST_DEL(&pstNode->list);
            /* get the first free node */
            return pstNode;
        }
    }

    if (GDC_NODE_8_SIZE == iBlockSize)
    {
        GDC_LIST_EACH_ENTRY_SAFE(pstNode, pstNextNode, &g_stGDCFreeNode[iThreadID].list_node8, list)
        {
            GDC_LIST_DEL(&pstNode->list);
            /* get the first free node */
            return pstNode;
        }
    }

    return NULL;
}

static inline GDCNode_ST *__GDC_GetLevel2Cache(INT32 iBlockSize)
{
    GDCNode_ST *pstNode = NULL;

    return pstNode;
}

static GDCNode_ST *GDC_GetLevel2Cache(INT32 iBlockSize)
{
    GDCNode_ST *pstNode = NULL;

    /* get lock */

    pstNode = __GDC_GetLevel2Cache(iBlockSize);

    /* release lock */

    return pstNode;
}
    
static GDCNode_ST *GDC_GetOneCache(INT32 iThreadID, INT32 iBlockSize)
{
    GDCNode_ST *pstNode = NULL;

    pstNode = GDC_GetLevel1Cache(iThreadID, iBlockSize);
    if (NULL == pstNode)
    {
        pstNode = GDC_GetLevel2Cache(iBlockSize);
    }

    if (NULL == pstNode)
    {
        GDC_ERROR("Can not get free %d node!", iBlockSize);
    }

    return pstNode;
}

static INT32 GDC_AddCacheList(GDC_ST *pstGDC, INT32 iThreadID, INT32 iBlock8Num, INT32 iBlock32Num)
{
    INT32 i = 0;
    GDCNode_ST *pstNode = NULL;

    /* block 8 and 32 num must not 0 at the same time */
    GDC_ASSERT(iBlock8Num || iBlock32Num);
   
    /* add block 32 to list */
    if (iBlock32Num)
    {
        for (i = 0; i < iBlock32Num; ++i)
        {
            pstNode = GDC_GetOneCache(iThreadID, GDC_NODE_32_SIZE);
            if (pstNode)
            {
                GDC_LIST_ADD_TAIL(&pstNode->list, &pstGDC->list);
                /* set cache data begin pointer */
                if ((0 == i) && (0 == pstGDC->uiCacheUse))
                {
                    pstGDC->pcCache = pstNode->pcMem;
                }
            } else
            {
                /* release already add blocks */
                GDC_FreeCache(pstGDC);
                return GDC_ERR;
            }
        }
    }
    else /* add one block 8 to list */
    {
        /* only one block 8 in cache list, it is the first */
        GDC_ASSERT(1 == iBlock8Num);
        
        pstNode = GDC_GetOneCache(iThreadID, GDC_NODE_8_SIZE);
        if (pstNode)
        {
            GDC_LIST_ADD_TAIL(&pstNode->list, &pstGDC->list);
            /* set cache data begin pointer */
            pstGDC->pcCache = pstNode->pcMem;
        } else
        {
            return GDC_ERR;
        }
    }

    return GDC_OK;
}
 
static INT32 GDC_FillDataToCache(GDC_ST *pstGDC, const CHAR * const pcBuf, UINT32 uiLen)
{
    /* pstGDC->pcCache */
    return GDC_OK;
}

UINT32 GDC_Append(GDC_ST *pstGDC, const CHAR * const pcBuf, UINT32 uiLen)
{
    INT32 iRet = 0;
    INT32 uiAppendLen = 0;
    INT32 iBlock8Num = 0;
    INT32 iBlock32Num = 0;

    /* TODO: use the real thread id */
    INT32 iThreadID = 0;

    /**
     * different len get blocks num
     * len < 8 :
     *     uiIsEight = 0 : get 1 8block
     *     uiIsEight = 1 : get 1 32block
     *
     * len >= 8 : get n 32blocks
     *
     */ 
    uiAppendLen = uiLen - pstGDC->uiCacheLeft;
    if (uiAppendLen > 0) /* need get new node */
    {
        if (uiAppendLen < 8) 
        {
            if (pstGDC->uiIsEight)
            {
                iBlock32Num = 1;
            } else {
                iBlock8Num = 1;
            }
        } else
        {
            iBlock32Num = (uiAppendLen % GDC_NODE_32_SIZE) ? (uiAppendLen / GDC_NODE_32_SIZE + 1) : (uiAppendLen / GDC_NODE_32_SIZE);
        }

        /* get free cache from level1 and level2 cache */
        iRet = GDC_AddCacheList(pstGDC, iThreadID, iBlock8Num, iBlock32Num);
        if (GDC_ERR == iRet)
        {
            /* get cache node fail, no data append to cache */
            return 0;
        }
    }

    /* file data to cache list */
    iRet = GDC_FillDataToCache(pstGDC, pcBuf, uiLen);

    return iRet;
}

void GDC_Dump(void)
{
    INT32 i = 0;
    GDCNode_ST *pstNode = NULL;
    
    /* dump free node */
    for (i = 0; i < NGE_MAX_THREAD; ++i)
    {
        printf("\r\nFree Node [%d]: \r\n", i);
        printf("Cache Mem: %p\r\n", g_stGDCFreeNode[i].pcCache);
        printf("Cache Node: \r\n");
        GDC_LIST_EACH_ENTRY(pstNode, &g_stGDCFreeNode[i].list_node8, list)
        {
            printf("[%d]%p ", pstNode->enType, pstNode->pcMem);
        } 
        
        GDC_LIST_EACH_ENTRY(pstNode, &g_stGDCFreeNode[i].list_node32, list)
        {
            printf("[%d]%p ", pstNode->enType, pstNode->pcMem);
        } 
    }

    printf("\r\n\r\n");

}


